#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <stdio.h>
#include <opencv2/opencv.hpp>

cv::Scalar bg_color(255, 255, 255);

void update(cv::Mat& image)
{
    const int w = image.cols;
    const int h = image.rows;

    cv::Scalar red(0, 0, 255);
    cv::Scalar green(0, 255, 0);
    cv::Scalar blue(255, 0, 0);
    const int thickness_dotted = -1;
    const int thickness_axis = 1;
    
    //           C
    // 
    //  A        O        B
    //
    //           D

    // plot x axis and y axis
    cv::Point A(0, h / 2);
    cv::Point B(w - 1, h / 2);
    cv::Point C(w / 2, 0);
    cv::Point D(w / 2, h - 1);

    cv::Point O(w / 2, h / 2);

    cv::Scalar black(0);

    cv::line(image, A, B, black, thickness_axis);
    cv::line(image, C, D, black, thickness_axis);

    // plot x ticks and y ticks
    int unit_len = 50;
    int x_n = w / unit_len;
    int y_n = h / unit_len;

    int radius = 2;
    int offx = 0;
    int offy = 0;
    for (int i = 0; i < x_n; i++)
    {
        offx = i * unit_len;

        cv::Point pA(O.x + offx, O.y);
        cv::circle(image, pA, radius, black, thickness_dotted);

        cv::Point pB(O.x - offx, O.y);
        cv::circle(image, pB, radius, black, thickness_dotted);
    }

    for (int i = 0; i < y_n; i++)
    {
        offy = i * unit_len;

        cv::Point pC(O.x, O.y + offy);
        cv::circle(image, pC, radius, black, thickness_dotted);

        cv::Point pD(O.x, O.y - offy);
        cv::circle(image, pD, radius, black, thickness_dotted);
    }
    
    // plot y = sin(x) curve
    for (float x = -6.29; x < 6.29; x += 0.01)
    {
        int xx = x * unit_len;
        float y = sin(x);
        int yy = y * unit_len;
        cv::Point p(O.x + xx, O.y + yy);
        cv::circle(image, p, 1, red, -1, thickness_dotted);
    }

    // plot r = a e ^ {b theta}
    // the value of a and b are tuned manually
    {
        const float a = 0.1;
        const float b = 0.05;
        for (float theta = 0.0; theta < 360; theta += 0.01)
        {
            float r = a * exp(b * theta);
            float x = r * cos(theta);
            float y = r * sin(theta);
            cv::Point p(O.x + x, O.y + y);
            cv::circle(image, p, 1, green, -1, thickness_dotted);
        }
    }

    // plot r = c * theta
    {
        const float c = 10;
        for (float theta = 0.0; theta < 360; theta += 0.01)
        {
            float r = c * theta;
            float x = r * cos(theta);
            float y = r * sin(theta);
            cv::Point p(O.x + x, O.y + y);
            cv::circle(image, p, 1, blue, -1, thickness_dotted);
        }

        // or equally, iteratively compute r:
        // double r = 0;
        // for (float theta = 0.0; theta < 360; theta += 0.01)
        // {
        //     float x = r * cos(theta);
        //     float y = r * sin(theta);
        //     cv::Point p(O.x + x, O.y + y);
        //     cv::circle(image, p, 1, blue, -1, thickness_dotted);
        //     r += 0.1;
        // }
    }
}

int main()
{
    const int w = 640;
    const int h = 480;
    cv::Size size(w, h);
    cv::Mat image(size, CV_8UC3);

    image = bg_color;

    update(image);

    cv::imshow("image", image);
    cv::waitKey();

    return 0;
}

